#! /usr/bin/env python3
# -*- coding: utf-8 -*-

import logging
import requests
import sys
import os
from optparse import OptionParser
import json
from colorama import Fore
from colorama import Style
import random
import string
import functools
import pickle
from datetime import datetime

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(name)s - %(levelname)-8s - %(threadName)s - %(message)s',
    filename="/tmp/unittest.log"
)

logging.info("============ BEGIN ============")


HEADERS = { 'Content-Type': 'application/json',
            'Accept': 'application/json' }

SESSION_INFO_FILE = '/tmp/session.info'

def _login(host, username='cisco', password='cisco123!'):
    url = "https://{}/controller/doLogin".format(host)
    session = requests.Session()
    r = session.post(url,
                     json={"userName": username, "password": password},
                     verify=False,
                     headers={'Content-Type': 'application/json', 'Accept': 'application/json'})
    logging.debug("Response headers: {}".format(r.headers))
    logging.debug("Response cookies: {}".format(r.cookies))
    if not r.ok:
        logging.error("Failed to login with {}".format(url))
        print("{}{}{}".format(Fore.RED, r.status_code, Style.RESET_ALL), file=sys.stderr)
        sys.exit(1)

    session.headers = {**HEADERS, "X-XSRF-TOKEN": session.cookies['XSRF-TOKEN']}
    return session


def _refreshable(func):
    @functools.wraps(func)
    def wrapper(self, *args, **kwargs):
        r = func(self, *args, **kwargs)

        if r.status_code == 401:
            logging.debug("Session outdated, should be refreshed")
            self.refresh()
            return func(self, *args, **kwargs)
        return r
    return wrapper


class NmSession:

    def __init__(self, host, username='cisco', password='cisco123!'):
        self.host = host
        self.username = username
        self.password = password

        if os.path.isfile(SESSION_INFO_FILE):
            with open(SESSION_INFO_FILE, 'rb') as f:
                self.session = pickle.load(f)
                self.session.verify = False
        else:
            self.refresh()


    def refresh(self):
        self.session = _login(self.host, self.username, self.password)
        with open(SESSION_INFO_FILE, 'wb') as f:
            pickle.dump(self.session, f)


    @_refreshable
    def get(self, *args, **kwargs):
        return self.session.get(*args, **kwargs)

    @_refreshable
    def post(self, *args, **kwargs):
        return self.session.post(*args, **kwargs)

    @_refreshable
    def put(self, *args, **kwargs):
        return self.session.put(*args, **kwargs)

    @_refreshable
    def delete(self, *args, **kwargs):
        return self.session.delete(*args, **kwargs)

    @_refreshable
    def patch(self, *args, **kwargs):
        return self.session.patch(*args, **kwargs)



def session(host, username=None, password=None):
    if not username:
        username = 'cisco'
    if not password:
        password = 'cisco123!'
    return NmSession(host, username, password)
    #return _login(host, username, password)

# def parser_options():
#     parser = OptionParser(usage="%prog [options] arg")
#     parser.add_option('--host', dest='host', default="10.74.68.149", help="address")
#     options, args = parser.parse_args()
#     return (options, args)


def parser():
    parser = OptionParser(usage="%prog [options] arg")
    if os.getenv('NM_HOST'):
        parser.add_option('--host', dest='host', default=os.getenv('NM_HOST'), help="address")
    return parser


def parse(parser, required=None):
    if not required:
        required = ['host']

    if not 'host' in required:
        required.append('host')

    (options, args) = parser.parse_args()
    for o in required:
        try:
            if not options.__getattribute__(o):
                parser.error("Must have option '{}'".format(o))
        except Exception:
            parser.error("Must have option '{}'".format(o))

    return (options, args)


def print_response(r):
    if not r.ok:
        print("{}{}{}".format(Fore.RED, r.status_code, Style.RESET_ALL), file=sys.stderr)
        # print("{}{}{}".format(Fore.RED, r.json(), Style.RESET_ALL), file=sys.stderr)
        print("{}{}{}".format(Fore.RED, json.dumps(r.json(), indent=4), Style.RESET_ALL), file=sys.stderr)
        sys.exit(1)

    if r.content:
        try:
            print(json.dumps(r.json(), indent=4))
        except Exception:
            print(r.text)




def get_json_from_response(r):
    if not r.ok:
        print("{}{}{}".format(Fore.RED, r.status_code, Style.RESET_ALL), file=sys.stderr)
        sys.exit(1)

    if r.content:
        return r.json()



def getRandomStr(n):
    return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(n))


class RestAgent:

    @classmethod
    def getAgent(cls):
        if hasattr(cls, 'agent'):
            return cls.agent
        cls.agent = cls()
        cls.agent.host = os.getenv('NM_HOST')
        cls.agent.session = session(host=cls.agent.host,
                                    username=os.getenv("NM_USERNAME"),
                                    password=os.getenv("NM_PASSWORD")
        )
        return cls.agent


    def _get(self, url, params={}, headers={}):
        return self.session.get(url, params=params, headers=headers)


    def _patch(self, url, params={}, payload={}, headers={}):
        return self.session.patch(url, params=params, json=payload, headers=headers)


    def _post(self, url, params={}, payload={}, headers={}):
        return self.session.post(url, params=params, json=payload, headers=headers)

    def _put(self, url, params={}, payload={}, headers={}):
        return self.session.put(url, params=params, json=payload, headers=headers)

    def _delete(self, url, params={}, headers={}, payload={}):
        return self.session.delete(url, params=params, headers=headers, json=payload)



def warn(s):
    trace = "\n{} WARN - {}".format(str(datetime.now()), s)
    print("{}{}{}".format(Fore.YELLOW, trace, Style.RESET_ALL), file=sys.stdout)

def info(s):
    trace = "\n{} INFO - {}".format(str(datetime.now()), s)
    print(trace)

def error(s):
    trace = "\n{} ERROR - {}".format(str(datetime.now()), s)
    print("{}{}{}".format(Fore.RED, trace, Style.RESET_ALL), file=sys.stderr)
